package edu.gatech.cc.CFAWaffleFriesWheresMyStuff.common;

import java.util.List;

import edu.gatech.cc.CFAWaffleFriesWheresMyStuff.common.Datastore.CanNotDeleteSelfException;
import edu.gatech.cc.CFAWaffleFriesWheresMyStuff.common.Datastore.UserDoesNotExistException;
import edu.gatech.cc.CFAWaffleFriesWheresMyStuff.common.Datastore.UserIsNotAdminException;

/**
 ** The class that handles the logging in and registration of users.
 ** Interacts with a database of Users.
 ** @author Dakota Lindsey
 */
public class UserManager {
	
	Datastore database;
	final int LOGIN_ATTEMPT_LIMIT;
	
	public UserManager(Datastore ds)
	{
		database = ds;
		LOGIN_ATTEMPT_LIMIT = 3;
	}
	
	/**
	 ** Attempts to login a User with the given username and password.
	 ** Will throw an exception if the username cannot be found or if the password is incorrect.
	 ** @param username The username of the User to be logged in
	 ** @param password The entered password for a given User
	 ** @return u The User associated with the given username and password
	 */
	public User attemptLogin(String username, String password)
		throws IncorrectPasswordException, UserDoesNotExistException, LoginAttemptLimitExceededException
	{
		//System.out.println("Login attempted!");
		
		User u = database.getUser(username);
		if(u.getLockStatus())
		{
			throw new LoginAttemptLimitExceededException();
		}
		if(u.checkPassword(password) == false)
		{
			u.loginFailed();
			database.saveUser(u);
			throw new IncorrectPasswordException();
		}
		return u;	
	}
	
	/**
	 ** Attempts to create a new User with the given information.
	 ** Will throw an exception if there is missing data, there are invalid characters, or if the user already exists.
	 ** @param username The username of the User to be registered
	 ** @param password The password of the User to be registered
	 ** @param name The name of the User to be registered
	 ** @param email The email address of the user to be registered
	 ** @param phoneNumber The phone number of the user to be registered
	 ** @return u The newly created User
	 */
	public User attemptRegister(String username, String password, String name, String email, String phoneNumber) 
			throws EmptyFieldException, UserExistsException, InvalidCharacterException
	{
		if (username.equals("") || password.equals("") || name.equals("") || phoneNumber.equals("") || email.equals(""))
		{
			throw new EmptyFieldException();
		}
		else if (checkInvalidChars(username))
		{
			throw new InvalidCharacterException();
		}
		else
		{
			if(database.userExists(username))
			{
				throw new UserExistsException();
			}
			User newUser = new User(username, password, name, phoneNumber, email);
			database.saveUser(newUser);
			return newUser;
		}
	}
	
	/**
	 ** Attempts to create a new Admin with the given information.
	 ** Will throw an exception if there is missing data, there are invalid characters, or if the username already exists.
	 ** @param username The username of the User to be registered
	 ** @param password The password of the User to be registered
	 ** @param name The name of the User to be registered
	 ** @param email The email address of the user to be registered
	 ** @param phoneNumber The phone number of the user to be registered
	 ** @returnThe newly created Admin
	 */
	public Admin attemptRegisterAdmin(String username, String password, String name, String email, String phoneNumber) 
			throws EmptyFieldException, UserExistsException, InvalidCharacterException
	{
		if (username.equals("") || password.equals("") || name.equals("") || phoneNumber.equals("") || email.equals(""))
		{
			throw new EmptyFieldException();
		}
		else if (checkInvalidChars(username))
		{
			throw new InvalidCharacterException();
		}
		else
		{
			if(database.userExists(username))
			{
				throw new UserExistsException();
			}
			Admin newAdmin = new Admin(username, password, name, phoneNumber, email);
			database.saveAdmin(newAdmin);
			return newAdmin;
		}
	}
	
	/**
	 * Returns a User instance filled with the information from the database with the given username.
	 * @param username The username of the user to get info for
	 * @return A User instance for the user.
	 * @throws UserDoesNotExistException Thrown if the given username cannot be found in the database.
	 */
	public User getUser(String username) throws UserDoesNotExistException
	{
		return database.getUser(username);
	}
	
	/**
	 * Gets a list of all usernames in the system.
	 * @return The list of all usernames in the system
	 */
	public List<String> getAllUsernames()
	{
		return database.getAllUsernames();
	}
	
	/**
	 * Sets the lock state of the given user to false in the database.
	 * @param username The username of the user you want to unlock
	 * @throws UserDoesNotExistException Thrown if the database cannot find any users with the given username.
	 */
	public void unlockUser(String username) throws UserDoesNotExistException
	{
		database.unlock(username);
	}
	
	/**
	 * Sets the isAdmin attribute of the given user to true in the database
	 * @param username The username of the user to upgrade
	 * @throws UserDoesNotExistException Thrown if the database cannot find any users with the given username.
	 */
	public void turnUserToAdmin(String username) throws UserDoesNotExistException
	{
		database.turnUserToAdmin(username);
	}
	
	/**
	 * Removes a user account from the records of the system.
	 * @param username The username of the user you want to delete
	 * @throws UserDoesNotExistException Thrown if the database cannot find any users with the given username.
	 * @throws CanNotDeleteSelfException Thrown if the admin tries to delete its own account.
	 * @throws UserIsNotAdminException Thrown if the given adminname is not actually an admin.
	 */
	public void deleteUser(String username, String adminname) throws UserDoesNotExistException, UserIsNotAdminException, CanNotDeleteSelfException
	{
		database.deleteUser(username, adminname);
	}
	
	/**
	 ** Checks an invalid character string to see if the entered information is flawed
	 ** Will return true if any of the information contains invalid characters
	 ** @param username The username of the User to be logged in
	 ** @return isInvalid A boolean indicating whether or not the username is a valid one
	 */
	private boolean checkInvalidChars(String username)
	{
		boolean isInvalid = false;
		String invalid = "!#$%^&*(){}[]<>\n\t\\|\"\',/?+=~`:;";
		for(int i = 0; i < invalid.length(); i++)
		{
			if(username.contains(invalid.substring(i, i+1)))
			{
				isInvalid = true;
				break;
			}
		}
		return isInvalid;
	}
	
	/**
	 ** An exception class thrown when an incorrect password is entered.
	 */
	public class IncorrectPasswordException extends Exception
	{
		/**
		 * Auto-generated default serialVersionUID
		 */
		private static final long serialVersionUID = 1L;	
	}
	
	/**
	 ** An exception class thrown when a data field is empty.
	 */
	public class EmptyFieldException extends Exception
	{
		/**
		 * Auto-generated default serialVersionUID
		 */
		private static final long serialVersionUID = 1L;	
	}
	
	/**
	 ** An exception class thrown when an entered username has invalid characters.
	 */
	public class InvalidCharacterException extends Exception
	{
		/**
		 * Auto-generated default serialVersionUID
		 */
		private static final long serialVersionUID = 1L;	
	}
	
	/**
	 * An exception class thrown when attempting to log in to an account with too many login attempts
	 */
	public class LoginAttemptLimitExceededException extends Exception
	{
		/**
		 * Auto-generated default serialVersionUID
		 */
		private static final long serialVersionUID = 1L;	
	}
	
	/**
	 * Thrown when an action is attempting to make a new user with a username that's already registered
	 * @author Matthew Barulic
	 *
	 */
	public class UserExistsException extends Exception {
		private static final long serialVersionUID = 1L;
	}
	
}

